/*******************************************************************************
 * Copyright (c) 2011 TXT e-solutions SpA
 * All rights reserved. This program and the accompanying materials
 * are made available under the terms of the Eclipse Public License v1.0
 * which accompanies this distribution, and is available at
 * http://www.eclipse.org/legal/epl-v10.html
 *
 * This work was performed within the IoT_at_Work Project
 * and partially funded by the European Commission's
 * 7th Framework Programme under the research area ICT-2009.1.3
 * Internet of Things and enterprise environments.
 *
 *
 * Authors:
 *     Cristoforo Seccia (TXT e-solutions SpA)
 *
 * Contributors:
 *      Domenico Rotondi (TXT e-solutions SpA)
 *******************************************************************************/
package it.txt.access.capability.verifier;

import it.txt.access.capability.commons.schema.validation.CapabilitySchemaValidationHandlerException;
import it.txt.access.capability.schema.AccessRightType;
import it.txt.access.capability.schema.AccessRightsCapabilityType;
import it.txt.access.capability.factory.CapabilitySchemaFactoryException;
import it.txt.access.capability.factory.CapabilitySchemaFactory;
import it.txt.access.capability.commons.signer.X509DocumentSigner;
import it.txt.access.capability.commons.utils.CalendarUtils;
import it.txt.access.capability.commons.signer.model.X509CertificateSubjectInfo;
import java.math.BigInteger;
import java.security.GeneralSecurityException;
import java.text.MessageFormat;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.xml.datatype.XMLGregorianCalendar;
import org.w3._2000._09.xmldsig_.X509IssuerSerialType;
import org.w3c.dom.Document;

/**
 * @author Cristoforo Seccia (TXT e-solutions SpA)
 * @author Domenico Rotondi (TXT e-solutions SpA)
 */
public class CapabilityVerifier {

    private static final Logger logger = Logger.getLogger(CapabilityVerifier.class.getName());

    /**
     * Capability validity check :: Not interested on the returned value
     */
    public static boolean accessRightsCapabilityValidityCheck(final AccessRightsCapabilityType $capabilityN,
            ArrayList<VerifiedCapability> vcs)
            throws CapabilitySchemaValidationHandlerException, CapabilityVerifierException {
        CapabilityVerifierValues values = new CapabilityVerifierValues();
        return accessRightsCapabilityValidityCheck($capabilityN, values, vcs);
    }

    /**
     * Capability validity check :: Interested on the returned value
     */
    public static boolean accessRightsCapabilityValidityCheck(final AccessRightsCapabilityType $capabilityN,
            CapabilityVerifierValues $valuesN, ArrayList<VerifiedCapability> vcs)
            throws CapabilitySchemaValidationHandlerException, CapabilityVerifierException {

        if (logger.isLoggable(Level.FINE))
            logger.fine(MessageFormat.format("Calling accessRightsCapabilityValidityCheck on Capability ID: {0}",
                $capabilityN.getAccessRightsCapabilityID()));
        
        X509CertificateSubjectInfo subjectInfo = new X509CertificateSubjectInfo();
        boolean rootCapability = false;

        try {
            // Check that the Capability has an Issuer
            if ($capabilityN.getIssuer() == null) {
                throw new CapabilityVerifierException(CapabilityVerifierErrorMessages.ISSUER_NULL);
            }
            // Check that the Capability has an Issuer value
            if ($capabilityN.getIssuer().getValue() == null) {
                throw new CapabilityVerifierException(CapabilityVerifierErrorMessages.ISSUER_NULL);
            }
            // Check that the Capability Issuer value is not empty
            if ($capabilityN.getIssuer().getValue().isEmpty()) {
                throw new CapabilityVerifierException(CapabilityVerifierErrorMessages.ISSUER_EMPTY);
            }
            // Check that the Capability has the resource ID
            if ($capabilityN.getResourceID() == null) {
                throw new CapabilityVerifierException(CapabilityVerifierErrorMessages.RESOURCE_ID_NULL);
            }
            // Check that the Capability Resource ID is not empty
            if ($capabilityN.getResourceID().isEmpty()) {
                throw new CapabilityVerifierException(CapabilityVerifierErrorMessages.RESOURCE_ID_EMPTY);
            }
            // Check that the Capability has the Capability ID
            if ($capabilityN.getAccessRightsCapabilityID() == null) {
                throw new CapabilityVerifierException(CapabilityVerifierErrorMessages.CAPABILITY_ID_NULL);
            }
            // Check that the Capability ID is not empty
            if ($capabilityN.getAccessRightsCapabilityID().isEmpty()) {
                throw new CapabilityVerifierException(CapabilityVerifierErrorMessages.CAPABILITY_ID_EMPTY);
            }
            // Check taht the Capability has an Issue Istant
            if ($capabilityN.getIssueInstant() == null) {
                throw new CapabilityVerifierException(CapabilityVerifierErrorMessages.ISSUE_ISTANT_NULL);
            }
            // Check that the Capability has a valid Issue Istant
            if (!$capabilityN.getIssueInstant().isValid()) {
                throw new CapabilityVerifierException(CapabilityVerifierErrorMessages.ISSUE_ISTANT_INVALID);
            }
            // Check that the capability has the Access Right Capability revocation Services
            if ($capabilityN.getAccessRightsCapabilityRevocationService() == null) {
                throw new CapabilityVerifierException(CapabilityVerifierErrorMessages.REVOCATION_SERVICES_NULL);
            }
            // Check that the capability has the Access Right Capability revocation Services
            if ($capabilityN.getAccessRightsCapabilityRevocationService().getRevocationServiceEndPoint() == null) {
                throw new CapabilityVerifierException(CapabilityVerifierErrorMessages.REVOCATION_SERVICES_NULL);
            }
            // Check that the capability has at least one Access Right Capability Revocation Service
            if ($capabilityN.getAccessRightsCapabilityRevocationService().getRevocationServiceEndPoint().isEmpty()) {
                throw new CapabilityVerifierException(CapabilityVerifierErrorMessages.REVOCATION_SERVICES_EMPTY);
            }
            // Check that the capability has the Access Rights
            if ($capabilityN.getAccessRights() == null) {
                throw new CapabilityVerifierException(CapabilityVerifierErrorMessages.ACCESS_RIGHTS_NULL);
            }
            // Check that the capability has the Access Rights
            if ($capabilityN.getAccessRights().getAccessRight() == null) {
                throw new CapabilityVerifierException(CapabilityVerifierErrorMessages.ACCESS_RIGHTS_NULL);
            }
            // Check that the capability has at least one Access Right
            if ($capabilityN.getAccessRights().getAccessRight().isEmpty()) {
                throw new CapabilityVerifierException(CapabilityVerifierErrorMessages.ACCESS_RIGHTS_EMPTY);
            }
            // Check the Capability has the validity conditions
            if ($capabilityN.getValidityCondition() == null) {
                throw new CapabilityVerifierException(CapabilityVerifierErrorMessages.VALIDITY_CONDITION_NULL);
            }
            // Check the Capability has the NotBefore date condition.
            if ($capabilityN.getValidityCondition().getNotBefore() == null) {
                throw new CapabilityVerifierException(CapabilityVerifierErrorMessages.NOT_BEFORE_CONDITION_NULL);
            }
            // Check that the Capability NotBefore date condition is valid
            if (!$capabilityN.getValidityCondition().getNotBefore().isValid()) {
                throw new CapabilityVerifierException(CapabilityVerifierErrorMessages.NOT_BEFORE_CONDITION_INVALID);
            }
            // Check the Capability has the NotOrOnAfter date condition.
            if ($capabilityN.getValidityCondition().getNotOnOrAfter() == null) {
                throw new CapabilityVerifierException(CapabilityVerifierErrorMessages.NOT_AFTER_CONDITION_NULL);
            }
            // Check that the Capability NotOrOnAfter date condition is valid
            if (!$capabilityN.getValidityCondition().getNotOnOrAfter().isValid()) {
                throw new CapabilityVerifierException(CapabilityVerifierErrorMessages.NOT_AFTER_CONDITION_INVALID);
            }

            XMLGregorianCalendar $capabilityN_issueIstant = $capabilityN.getIssueInstant();
            XMLGregorianCalendar $capabilityN_issueIstant_UTC = $capabilityN_issueIstant.normalize();

            XMLGregorianCalendar $capabilityN_notBefore = $capabilityN.getValidityCondition().getNotBefore();
            XMLGregorianCalendar $capabilityN_notBefore_UTC = $capabilityN_notBefore.normalize();

            XMLGregorianCalendar $capabilityN_notOnOrAfter = $capabilityN.getValidityCondition().getNotOnOrAfter();
            XMLGregorianCalendar $capabilityN_notOnOrAfter_UTC = $capabilityN_notOnOrAfter.normalize();

            int check_CapabilityN_IssuerInstant_And_CapabilityN_NotBefore =
                    check_CapabilityN_IssuerInstant_And_CapabilityN_NotBefore(
                    $capabilityN_issueIstant_UTC, $capabilityN_notBefore_UTC);

            int check_CapabilityN_NotBefore_And_CapabilityN_NotOrOnAfter =
                    check_CapabilityN_NotBefore_And_CapabilityN_NotOrOnAfter($capabilityN_notBefore_UTC, $capabilityN_notOnOrAfter_UTC);

            // Check the time constraints
            // For Human: If ( (issuer_Istant > notBefore) OR (notBefore >= notOrOnAfter) ) => Error
            if (check_CapabilityN_IssuerInstant_And_CapabilityN_NotBefore == CalendarUtils.AFTER_THAN
                    || (check_CapabilityN_NotBefore_And_CapabilityN_NotOrOnAfter == CalendarUtils.AFTER_THAN
                    || check_CapabilityN_NotBefore_And_CapabilityN_NotOrOnAfter == CalendarUtils.EQUAL_TO)) {

                throw new CapabilityVerifierException(CapabilityVerifierErrorMessages.VALIDITY_CONDITIONS_INVALID);
            }

            //Recover the $capabilityN as an XML Document
            Document $capabilityN_document = null;
            try {
                $capabilityN_document = CapabilitySchemaFactory.getInstance().createAccessRightsDocument($capabilityN);
            } catch (CapabilitySchemaFactoryException ex) {
                throw new CapabilityVerifierException(CapabilityVerifierErrorMessages.PARSE_CAPABILITY_ERROR, ex);
            }

            // Check that $capabilityN as an XML Document is not null
            if ($capabilityN_document == null) {
                throw new CapabilityVerifierException(CapabilityVerifierErrorMessages.PARSE_CAPABILITY_ERROR);
            }

            try {
                // Check the Capability has not been tampered!
                if (!X509DocumentSigner.verifyXMLElementSign($capabilityN_document.getDocumentElement(), subjectInfo)) {
                    throw new CapabilityVerifierException(CapabilityVerifierErrorMessages.SIGNATURE_INVALID);
                }
            } catch (GeneralSecurityException ex) {
                throw new CapabilityVerifierException(CapabilityVerifierErrorMessages.SIGNATURE_CHECK_ERROR, ex);
            }

            // Check if exists the Issuer Access Rights
            if ($capabilityN.getIssuerAccessRightsCapability() == null) {
                throw new CapabilityVerifierException(CapabilityVerifierErrorMessages.INNER_CAPABILITY_NULL);
            }

            // Check if this is the last in chain AccessRightsCapability
            if ($capabilityN.getIssuerAccessRightsCapability().getAccessRightsCapability() != null) {

                // The Capability under check is not the last in chain.
                // So check the referred Capability
                final AccessRightsCapabilityType $capabilityN1 =
                        $capabilityN.getIssuerAccessRightsCapability().getAccessRightsCapability();

                CapabilityVerifierValues $valuesN1 = new CapabilityVerifierValues();

                // Recursively call checking the Capability
                if (!(accessRightsCapabilityValidityCheck($capabilityN1, $valuesN1, vcs))) {
                    // The inner Capability chain is invalid!
                    throw new CapabilityVerifierException(CapabilityVerifierErrorMessages.INNER_CAPABILITY_INVALID);
                }

                //Check if the inner capability has no delegable access rights
                if ($valuesN1.getInnerAccessRights().isEmpty()) {
                    throw new CapabilityVerifierException(CapabilityVerifierErrorMessages.NO_ACCESS_RIGHTS_COPIED);
                }

                // The inner Capability chain is OK.
                // So complete the check of the Capability at hand

                //Check the InnerSubject is the Issuer of the Capability at hand!
                if (!($valuesN1.getInnerSubject().getSubjectID().getValue().equals($capabilityN.getIssuer().getValue()))) {
                    throw new CapabilityVerifierException(CapabilityVerifierErrorMessages.ISSUER_NOT_EQUAL_INNER_SUBJECT);
                }

                // Check the referred resources are the same
                if (!($valuesN1.getInnerResourceID().equals($capabilityN.getResourceID()))) {
                    throw new CapabilityVerifierException(CapabilityVerifierErrorMessages.RESOURCE_ID_NOT_EQUAL);
                }

                // Check the Capability IDs are different
                /*if ($valuesN1.getInnerCapabilityID().equals($capabilityN.getAccessRightsCapabilityID())) {
                throw new CapabilityVerifierException(CapabilityVerifierErrorMessages.CAPABILITY_ID_NOT_UNIQUE);
                }*/

                // Check the Revocation Services are the same
                List<String> $capabilityN_revocationServices =
                        $capabilityN.getAccessRightsCapabilityRevocationService().getRevocationServiceEndPoint();
                String[] arrayOne = (String[]) $capabilityN_revocationServices.toArray(new String[0]);

                List<String> $valuesN1_revocationServices =
                        $valuesN1.getInnerAccessRightsCapabilityRevocationServices();
                String[] arrayTwo = (String[]) $valuesN1_revocationServices.toArray(new String[0]);

                if (!Arrays.equals(arrayOne, arrayTwo)) {
                    throw new CapabilityVerifierException(CapabilityVerifierErrorMessages.REVOCATION_SERVICES_NOT_EQUALS);
                }

                // Check the Access Rights are correct
                List<AccessRightType> $capabilityN_accessRights = $capabilityN.getAccessRights().getAccessRight();
                List<AccessRightType> $valuesN1_accessRights = $valuesN1.getInnerAccessRights();

                if (!checkAccessRightsCompatibility($valuesN1_accessRights, $capabilityN_accessRights)) {
                    throw new CapabilityVerifierException(CapabilityVerifierErrorMessages.ACCESS_RIGHTS_INCOMPATIBLE);
                }

                // Check the time constraints
                XMLGregorianCalendar $valuesN1_NotBefore = $valuesN1.getInnerValidityCondition().getNotBefore();
                XMLGregorianCalendar $valuesN1_NotBefore_UTC = $valuesN1_NotBefore.normalize();

                XMLGregorianCalendar $valuesN1_NotOnOrAfter = $valuesN1.getInnerValidityCondition().getNotOnOrAfter();
                XMLGregorianCalendar $valuesN1_NotOnOrAfter_UTC = $valuesN1_NotOnOrAfter.normalize();

                int check_ValuesN1_NotBefore_And_CapabilityN_NotBefore =
                        check_ValuesN1_NotBefore_And_CapabilityN_NotBefore(
                        $valuesN1_NotBefore_UTC, $capabilityN_notBefore_UTC);

                int check_CapabilityN_NotOrOnAfter_And_ValuesN1_NotOrOnAfter =
                        check_CapabilityN_NotOrOnAfter_And_ValuesN1_NotOrOnAfter(
                        $capabilityN_notOnOrAfter_UTC, $valuesN1_NotOnOrAfter_UTC);

                // Check the time constraints
                // For Human: If ( (inner_notBefore > capability_notBefore) OR (capability_notOrOnAfter > inner_notOnOrAfter) ) => Error
                if (check_ValuesN1_NotBefore_And_CapabilityN_NotBefore == CalendarUtils.AFTER_THAN
                        || check_CapabilityN_NotOrOnAfter_And_ValuesN1_NotOrOnAfter == CalendarUtils.AFTER_THAN) {

                    throw new CapabilityVerifierException(CapabilityVerifierErrorMessages.INNER_VALIDITY_CONDITIONS_INVALID);
                }

                /*
                 * Check that the x509 certificate serial number and issuer name of
                 * the nested capability are the same as the ones specified in the current capability.
                 */
                if ($valuesN1.getX509SubjectInfo() != null) {

                    X509CertificateSubjectInfo innerInfo = $valuesN1.getX509SubjectInfo();
                    BigInteger innerInfoSerial = innerInfo.getX509SerialNumber();
                    String innerInfoIssuer = innerInfo.getX509IssuerName();
                    String innerInfoSbj = innerInfo.getX509SubjectName();

                    innerInfoIssuer = innerInfoIssuer.trim().replace(" ", "");
                    innerInfoIssuer = innerInfoIssuer.toUpperCase();

                    if (logger.isLoggable(Level.FINER))
                        logger.finer(MessageFormat.format("Capability Issuer Name: {0} - Capability Serial Number: {1}",
                            new Object[]{innerInfoIssuer, innerInfoSerial.toString(16)}));

                    BigInteger currentSerial = subjectInfo.getX509SerialNumber();
                    String currentIssuer = subjectInfo.getX509IssuerName();
                    currentIssuer = currentIssuer.trim().replace(" ", "");
                    currentIssuer = currentIssuer.toUpperCase();
                    if (logger.isLoggable(Level.FINER))
                        logger.finer(MessageFormat.format("Cert Issuer Name: {0} - Sert Serial Number: {1}",
                            new Object[]{currentIssuer, currentSerial.toString(16)}));

                    if (!innerInfoIssuer.equals(currentIssuer)) {
                        throw new CapabilityVerifierException(CapabilityVerifierErrorMessages.X509_SUBJECTS_INCONSISTENT);
                    }
                    if (!innerInfoSerial.equals(currentSerial)) {
                        throw new CapabilityVerifierException(CapabilityVerifierErrorMessages.X509_SUBJECTS_INCONSISTENT);
                    }
                }

            } else {
                rootCapability = true;
                // Check the Issuer and Subject are the same
                if (!$capabilityN.getIssuer().getValue().equals($capabilityN.getSubject().getSubjectID().getValue())) {
                    throw new CapabilityVerifierException(CapabilityVerifierErrorMessages.ISSUER_NOT_EQUAL_SUBJETC);
                }
                // Check the AccessRights
                if (!checkAccessRights($capabilityN.getAccessRights().getAccessRight())) {
                    throw new CapabilityVerifierException();
                }
            }

            if (rootCapability) {
                $valuesN.setX509SubjectInfo(subjectInfo);
            } else {
                subjectInfo = recoverX509SubjectInfoFromCapability($capabilityN);
                if (subjectInfo == null) {
                    if (logger.isLoggable(Level.FINER))
                        logger.finer("No X509 Subject key Info found.");
                } else {
                    $valuesN.setX509SubjectInfo(subjectInfo);
                }
            }

            // The Capability is OK. Prepare the required output values and exit
            $valuesN.setInnerCapabilityID($capabilityN.getAccessRightsCapabilityID());
            $valuesN.setInnerSubject($capabilityN.getSubject());
            $valuesN.setInnerResourceID($capabilityN.getResourceID());
            $valuesN.setInnerValidityCondition($capabilityN.getValidityCondition());
            $valuesN.setInnerAccessRightsCapabilityRevocationServices(
                    $capabilityN.getAccessRightsCapabilityRevocationService().getRevocationServiceEndPoint());
            List<AccessRightType> $capabilityN_accessRights = $capabilityN.getAccessRights().getAccessRight();
            List<AccessRightType> $valuesN_accessRights = copyAccessRightsFromCapabiity($capabilityN_accessRights);
            $valuesN.setInnerAccessRights($valuesN_accessRights);

            VerifiedCapability vc = new VerifiedCapability();
            vc.setIssueDate($capabilityN.getIssueInstant().toGregorianCalendar().getTime());
            vc.setCapabilityID($capabilityN.getAccessRightsCapabilityID());
            vcs.add(vc);

            return true;

        } catch (CapabilityVerifierException ex) {
            throw new CapabilityVerifierException(ex.getMessage(), ex);
        }
    }

    private static boolean checkAccessRights(List<AccessRightType> $capabilityN_accessRights) throws CapabilityVerifierException {

        // Check that all access rights have a name
        boolean returnFlag = false;
        for (AccessRightType $capabilityN_accessRightType : $capabilityN_accessRights) {

            if ($capabilityN_accessRightType.getPermittedAction() == null) {
                throw new CapabilityVerifierException(CapabilityVerifierErrorMessages.ACCESS_RIGHT_PERMITTED_ACTION_NULL);
            }
            if ($capabilityN_accessRightType.getPermittedAction().getNamespace() == null) {
                throw new CapabilityVerifierException(CapabilityVerifierErrorMessages.ACCESS_RIGHT_PERMITTED_ACTION_NAMESPACE_NULL);
            }
            if ($capabilityN_accessRightType.getPermittedAction().getNamespace().isEmpty()) {
                throw new CapabilityVerifierException(CapabilityVerifierErrorMessages.ACCESS_RIGHT_PERMITTED_ACTION_NAMESPACE_EMPTY);
            }
            if ($capabilityN_accessRightType.getPermittedAction().getValue() == null) {
                throw new CapabilityVerifierException(CapabilityVerifierErrorMessages.ACCESS_RIGHT_PERMITTED_ACTION_VALUE_NULL);
            }
            if ($capabilityN_accessRightType.getPermittedAction().getValue().isEmpty()) {
                throw new CapabilityVerifierException(CapabilityVerifierErrorMessages.ACCESS_RIGHT_PERMITTED_ACTION_VALUE_EMPTY);
            }
            if ($capabilityN_accessRightType.getDelegable() == null) {
                throw new CapabilityVerifierException(CapabilityVerifierErrorMessages.ACCESS_RIGHT_DELEGABLE_NULL);
            }
            returnFlag = returnFlag || $capabilityN_accessRightType.getDelegable().isValue();
        }
        // Check at least one access right has the DelegableRightFlag set!
        if (!returnFlag) {
            throw new CapabilityVerifierException(CapabilityVerifierErrorMessages.ACCESS_RIGHTS_DELEGABLE_EMPTY);
        }
        return true;
    }

    private static boolean checkAccessRightsCompatibility(List<AccessRightType> $valuesN1_accessRights,
            List<AccessRightType> $capabilityN_accessRights) throws CapabilityVerifierException {

        HashMap<String, AccessRightType> $valuesN1_HashMap = new HashMap<String, AccessRightType>();
        for (AccessRightType $valuesN1_accessRightType : $valuesN1_accessRights) {
            $valuesN1_HashMap.put($valuesN1_accessRightType.getPermittedAction().getValue(), $valuesN1_accessRightType);
        }

        for (AccessRightType $capabilityN_accessRightType : $capabilityN_accessRights) {

            AccessRightType $valuesN1_accessRightType =
                    $valuesN1_HashMap.get($capabilityN_accessRightType.getPermittedAction().getValue());

            boolean $valuesN1_accessRightType_delegable = $valuesN1_accessRightType.getDelegable().isValue();

            boolean $valuesN1_accessRightType_delegation_depth =
                    ($valuesN1_accessRightType.getDelegable().getDelegationDepth() == null)
                    || ($valuesN1_accessRightType.getDelegable().getDelegationDepth().intValue() > 0);

            if (!($valuesN1_accessRightType_delegable && $valuesN1_accessRightType_delegation_depth)) {
                throw new CapabilityVerifierException("An inner Access Right is null or is is not delegable");
            }
        }

        return true;
    }

    private static List<AccessRightType> copyAccessRightsFromCapabiity(List<AccessRightType> $capabilityN_accessRights) {

        List<AccessRightType> $valuesN_accessRights = new ArrayList<AccessRightType>();

        for (AccessRightType $capabilityN_accessRight : $capabilityN_accessRights) {

            //If is not delegable - then skip
            if (!$capabilityN_accessRight.getDelegable().isValue()) {
                continue;
            }

            //If is delegable but the delegation_depth (if exists) is == 0 - then skip
            if ($capabilityN_accessRight.getDelegable().getDelegationDepth() != null
                    && $capabilityN_accessRight.getDelegable().getDelegationDepth().intValue() == 0) {
                continue;
            }

            //Otherwise the action right: (isDelegable) and (delegation_depth == null or delegation_depth >=1 )

            AccessRightType $valuesN_art = new AccessRightType();

            $valuesN_art.setPermittedAction($capabilityN_accessRight.getPermittedAction());
            $valuesN_art.setConditions($capabilityN_accessRight.getConditions());

            AccessRightType.Delegable $valuesN_delegable = new AccessRightType.Delegable();

            if ($capabilityN_accessRight.getDelegable().getDelegationDepth() == null) {
                //Set only the delegable_value to true leaving the delegable_depth undefined
                $valuesN_delegable.setValue(true);
            } else {
                int $valuesN_DelegationDepth = $capabilityN_accessRight.getDelegable().getDelegationDepth().intValue();
                //Decrease the delegation_depth
                $valuesN_DelegationDepth--;
                BigInteger bigIntValue = new BigInteger(Integer.toString($valuesN_DelegationDepth));
                //Set the delegation_depth
                $valuesN_delegable.setDelegationDepth(bigIntValue);
                //Set the delegable_value if the decremented delegable_depth is > 0
                $valuesN_delegable.setValue($valuesN_DelegationDepth > 0);
            }
            $valuesN_art.setDelegable($valuesN_delegable);

            //Add the access right to the list
            $valuesN_accessRights.add($valuesN_art);
        }
        return $valuesN_accessRights;
    }

    private static X509CertificateSubjectInfo recoverX509SubjectInfoFromCapability(AccessRightsCapabilityType $capabilityN) {
        X509CertificateSubjectInfo subjectInfo = null;
        if ($capabilityN.getSubject().getSubjectKeyInfo() != null) {
            if ($capabilityN.getSubject().getSubjectKeyInfo().getX509IssuerSerial() != null) {
                X509IssuerSerialType xist = $capabilityN.getSubject().getSubjectKeyInfo().getX509IssuerSerial();
                subjectInfo = new X509CertificateSubjectInfo();
                subjectInfo.setX509SubjectName($capabilityN.getSubject().getSubjectID().getValue());
                subjectInfo.setX509IssuerName(xist.getX509IssuerName());
                subjectInfo.setX509SerialNumber(xist.getX509SerialNumber());
            }
        }
        return subjectInfo;
    }

    private static int check_CapabilityN_IssuerInstant_And_CapabilityN_NotBefore(XMLGregorianCalendar capabilityN_issueIstant_UTC, XMLGregorianCalendar capabilityN_notBefore_UTC) {

        int check_CapabilityN_IssuerInstant_And_CapabilityN_NotBefore =
                CalendarUtils.comapareDataToAnotherDate(capabilityN_issueIstant_UTC,
                capabilityN_notBefore_UTC);

        switch (check_CapabilityN_IssuerInstant_And_CapabilityN_NotBefore) {
            case CalendarUtils.AFTER_THAN: {
                String msg = "The Capability Issuer istant Date (";
                msg += new SimpleDateFormat(CalendarUtils.DATE_PRINT_PATTERN).format(
                        capabilityN_issueIstant_UTC.toGregorianCalendar().getTime());
                msg += ") is 'after' then the Capability 'Not Before' Date (";
                msg += new SimpleDateFormat(CalendarUtils.DATE_PRINT_PATTERN).format(
                        capabilityN_notBefore_UTC.toGregorianCalendar().getTime());
                msg += ").";
                logger.log(Level.SEVERE, msg);
                break;
            }
            case CalendarUtils.BEFORE_THAN: {
                if (logger.isLoggable(Level.FINER)) {
                    String msg = "The Capability Issuer istant Date (";
                    msg += new SimpleDateFormat(CalendarUtils.DATE_PRINT_PATTERN).format(
                            capabilityN_issueIstant_UTC.toGregorianCalendar().getTime());
                    msg += ") is 'before' then the Capability 'Not Before' Date (";
                    msg += new SimpleDateFormat(CalendarUtils.DATE_PRINT_PATTERN).format(
                            capabilityN_notBefore_UTC.toGregorianCalendar().getTime());
                    msg += ").";
                    logger.finer(msg);
                }
                break;
            }
            case CalendarUtils.EQUAL_TO: {
                if (logger.isLoggable(Level.FINER)) {
                    String msg = "The Capability Issuer istant Date (";
                    msg += new SimpleDateFormat(CalendarUtils.DATE_PRINT_PATTERN).format(
                            capabilityN_issueIstant_UTC.toGregorianCalendar().getTime());
                    msg += ") is 'equal' to the Capability 'Not Before' Date (";
                    msg += new SimpleDateFormat(CalendarUtils.DATE_PRINT_PATTERN).format(
                            capabilityN_notBefore_UTC.toGregorianCalendar().getTime());
                    msg += ").";
                    logger.finer(msg);
//                    logger.log(Level.WARNING, msg);
                }
                break;
            }
        }
        return check_CapabilityN_IssuerInstant_And_CapabilityN_NotBefore;
    }

    private static int check_CapabilityN_NotBefore_And_CapabilityN_NotOrOnAfter(XMLGregorianCalendar capabilityN_notBefore_UTC, XMLGregorianCalendar capabilityN_notOrOnAfter) {

        int check_CapabilityN_NotBefore_And_CapabilityN_NotOrOnAfter =
                CalendarUtils.comapareDataToAnotherDate(capabilityN_notBefore_UTC,
                capabilityN_notOrOnAfter);

        switch (check_CapabilityN_NotBefore_And_CapabilityN_NotOrOnAfter) {
            case CalendarUtils.AFTER_THAN: {
                String msg = "The Capability 'Not Before' Date (";
                msg += new SimpleDateFormat(CalendarUtils.DATE_PRINT_PATTERN).format(
                        capabilityN_notBefore_UTC.toGregorianCalendar().getTime());
                msg += ") is 'after' then the Capability 'Not Or On After' Date (";
                msg += new SimpleDateFormat(CalendarUtils.DATE_PRINT_PATTERN).format(
                        capabilityN_notOrOnAfter.toGregorianCalendar().getTime());
                msg += ").";
                logger.log(Level.SEVERE, msg);
                break;
            }
            case CalendarUtils.BEFORE_THAN: {
                if (logger.isLoggable(Level.FINER)) {
                    String msg = "The Capability 'Not Before' Date (";
                    msg += new SimpleDateFormat(CalendarUtils.DATE_PRINT_PATTERN).format(
                            capabilityN_notBefore_UTC.toGregorianCalendar().getTime());
                    msg += ") is 'before' then the Capability 'Not Or On After' Date (";
                    msg += new SimpleDateFormat(CalendarUtils.DATE_PRINT_PATTERN).format(
                            capabilityN_notOrOnAfter.toGregorianCalendar().getTime());
                    msg += ").";
                    logger.finer(msg);
                }
                break;
            }
            case CalendarUtils.EQUAL_TO: {
                String msg = "The Capability 'Not Before' Date (";
                msg += new SimpleDateFormat(CalendarUtils.DATE_PRINT_PATTERN).format(
                        capabilityN_notBefore_UTC.toGregorianCalendar().getTime());
                msg += ") is 'equal' to the Capability 'Not Or On After' Date (";
                msg += new SimpleDateFormat(CalendarUtils.DATE_PRINT_PATTERN).format(
                        capabilityN_notOrOnAfter.toGregorianCalendar().getTime());
                msg += ").";
                logger.log(Level.SEVERE, msg);
                break;
            }
        }
        return check_CapabilityN_NotBefore_And_CapabilityN_NotOrOnAfter;
    }

    private static int check_ValuesN1_NotBefore_And_CapabilityN_NotBefore(XMLGregorianCalendar inner_notBefore,
            XMLGregorianCalendar capability_notBefore) {

        int check_ValuesN1_NotBefore_And_CapabilityN_NotBefore =
                CalendarUtils.comapareDataToAnotherDate(inner_notBefore,
                capability_notBefore);

        switch (check_ValuesN1_NotBefore_And_CapabilityN_NotBefore) {
            case CalendarUtils.AFTER_THAN: {
                String msg = "The inner Capability 'Not Before' Date (";
                msg += new SimpleDateFormat(CalendarUtils.DATE_PRINT_PATTERN).format(
                        inner_notBefore.toGregorianCalendar().getTime());
                msg += ") is 'after' then the outer Capability 'Not Before' Date (";
                msg += new SimpleDateFormat(CalendarUtils.DATE_PRINT_PATTERN).format(
                        capability_notBefore.toGregorianCalendar().getTime());
                msg += ").";
                logger.log(Level.SEVERE, msg);
                break;
            }
            case CalendarUtils.BEFORE_THAN: {
                if (logger.isLoggable(Level.FINER)) {
                    String msg = "The inner Capability 'Not Before' Date (";
                    msg += new SimpleDateFormat(CalendarUtils.DATE_PRINT_PATTERN).format(
                            inner_notBefore.toGregorianCalendar().getTime());
                    msg += ") is 'before' then the outer Capability 'Not Before' Date (";
                    msg += new SimpleDateFormat(CalendarUtils.DATE_PRINT_PATTERN).format(
                            capability_notBefore.toGregorianCalendar().getTime());
                    logger.finer(msg);
                }
                break;
            }
            case CalendarUtils.EQUAL_TO: {
                String msg = "The inner Capability 'Not Before' Date (";
                msg += new SimpleDateFormat(CalendarUtils.DATE_PRINT_PATTERN).format(
                        inner_notBefore.toGregorianCalendar().getTime());
                msg += ") is 'equal' to the outer Capability 'Not Before' Date (";
                msg += new SimpleDateFormat(CalendarUtils.DATE_PRINT_PATTERN).format(
                        capability_notBefore.toGregorianCalendar().getTime());
                logger.log(Level.SEVERE, msg);
                break;
            }
        }
        return check_ValuesN1_NotBefore_And_CapabilityN_NotBefore;
    }

    private static int check_CapabilityN_NotOrOnAfter_And_ValuesN1_NotOrOnAfter(XMLGregorianCalendar capability_notOrOnAfter,
            XMLGregorianCalendar inner_notOrOnAfter) {

        int check_CapabilityN_NotOrOnAfter_And_ValuesN1_NotOrOnAfter =
                CalendarUtils.comapareDataToAnotherDate(capability_notOrOnAfter,
                inner_notOrOnAfter);

        switch (check_CapabilityN_NotOrOnAfter_And_ValuesN1_NotOrOnAfter) {
            case CalendarUtils.AFTER_THAN: {
                String msg = "The outer Capability 'Not Or On After' Date (";
                msg += new SimpleDateFormat(CalendarUtils.DATE_PRINT_PATTERN).format(
                        capability_notOrOnAfter.toGregorianCalendar().getTime());
                msg += ") is 'after' then the inner Capability 'Not Or On After' Date (";
                msg += new SimpleDateFormat(CalendarUtils.DATE_PRINT_PATTERN).format(
                        inner_notOrOnAfter.toGregorianCalendar().getTime());
                msg += ").";
                logger.log(Level.SEVERE, msg);
                break;
            }
            case CalendarUtils.BEFORE_THAN: {
                if (logger.isLoggable(Level.FINER)) {
                    String msg = "The outer Capability 'Not Or On After' Date (";
                    msg += new SimpleDateFormat(CalendarUtils.DATE_PRINT_PATTERN).format(
                            capability_notOrOnAfter.toGregorianCalendar().getTime());
                    msg += ") is 'before' then the inner Capability 'Not Or On After' Date (";
                    msg += new SimpleDateFormat(CalendarUtils.DATE_PRINT_PATTERN).format(
                            inner_notOrOnAfter.toGregorianCalendar().getTime());
                    logger.finer(msg);
                }
                break;
            }
            case CalendarUtils.EQUAL_TO: {
                String msg = "The outer Capability 'Not Or On After' Date (";
                msg += new SimpleDateFormat(CalendarUtils.DATE_PRINT_PATTERN).format(
                        capability_notOrOnAfter.toGregorianCalendar().getTime());
                msg += ") is 'equal' to the inner Capability 'Not Or On After' Date (";
                msg += new SimpleDateFormat(CalendarUtils.DATE_PRINT_PATTERN).format(
                        inner_notOrOnAfter.toGregorianCalendar().getTime());
                logger.log(Level.SEVERE, msg);
                break;
            }
        }
        return check_CapabilityN_NotOrOnAfter_And_ValuesN1_NotOrOnAfter;
    }
}
